//#define USE_DUNGEONBUDDY_DLL

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonBehaviors.Actions;
using Styx;
using Styx.CommonBot;
using Styx.Patchables;
using Styx.TreeSharp;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWCache;
using Styx.WoWInternals.WoWObjects;
using Action = Styx.TreeSharp.Action;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Profiles;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Classic
#else
using Bots.DungeonBuddy.Profiles;
using Bots.DungeonBuddy.Attributes;
using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Classic
#endif
{
    #region Normal Difficulty

    public class ScarletMonastery : Dungeon
    {
        #region Overrides of Dungeon

        public override uint DungeonId
        {
            get { return 164; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(2920.317, -799.8921, 160.3323); }
        }

        public override WoWPoint ExitLocation
        {
            get { return new WoWPoint(1124.471, 504.2796, 0.9892024); }
        }

        public override void OnEnter()
        {
            _checkForZealot = false;
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            units.RemoveAll(
                ret =>
                {
                    var unit = ret as WoWUnit;
                    if (unit != null)
                    {
                        if (unit.Combat && !unit.TaggedByMe && !unit.IsTargetingMeOrPet && !unit.IsTargetingMyPartyMember)
                            return true;
                        if (unit.Entry == TrainingDummyId || unit.Entry == TrainingDummyId2)
                            return true;
                        // ignore npcs that are fighting other npcs
                        if ((unit.Entry == ScarletCenturionId || unit.Entry == ZombifiedCorpseId || unit.Entry == PileOfCorpsesId) && !unit.TaggedByMe && unit.Combat &&
                            Me.Combat)
                            return true;
                        // ignore brother korloff if he's near other unfriendlies
                        if (unit.Entry == BrotherKorloffId && !unit.Combat &&
                            ScriptHelpers.GetUnfriendlyNpsAtLocation(() => unit.Location, 30, u => u.Entry != BrotherKorloffId).Any())
                            return true;
                        // ignore brother korloff if I'm melee and he's casting firestorm kick
                        if (unit.Entry == BrotherKorloffId && unit.CastingSpellId == FirestormKickId && Me.IsMelee())
                            return true;
                        // kill this zealot before engaging CommanderDurand
                        if (_commanderDurandIds.Contains(unit.Entry) && !unit.Combat && _checkForZealot)
                            return true;
                    }
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var obj in incomingunits)
            {
                var unit = obj as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == ScarletZealot && !Me.Combat && Me.IsTank() && unit.Location.DistanceSqr(_scarletZealotLoc) <= 10 * 10)
                        outgoingunits.Add(unit);
                }
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit != null)
                {
                    if (unit.Entry == PileOfCorpsesId)
                        priority.Score += 300;

                    else if (unit.Entry == EvictedSoul)
                        priority.Score += 1000;
                    else if (unit.Entry == ThalnostheSoulrenderId && Me.IsDps())
                        priority.Score += 900;
                }
            }
        }

        #endregion

        private const uint PileOfCorpsesId = 59722;
        private const uint EvictedSoul = 59974;
        private const uint ScarletCenturionId = 59746;
        private const uint ZombifiedCorpseId = 59771;
        private const uint TrainingDummyId = 60197;
        private const uint TrainingDummyId2 = 64446;
        private const uint BrotherKorloffId = 59223;
        private const uint ScorchedEarth = 59507;
        private const uint ScarletZealot = 58590;
        private uint[] _commanderDurandIds = new uint[] { 60040 };
        private const int FirestormKickId = 113764;
        private const int UntoDustThouShaltReturnQuestId = 31516;
        private const uint HighInquisitorWhitemaneId = 3977;
        private const uint BladesOfTheAnointedId = 87390;
        private const uint BladeOfTheAnointedId = 64855; // WoWUnit version.

        private readonly WoWPoint _scarletZealotLoc = new WoWPoint(760.7621, 551.4827, 12.80546);
        private bool _checkForZealot;

        private static LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        [EncounterHandler(0)]
        public Composite RootBehavior()
        {
            WoWUnit whiteMane = null;
            PlayerQuest untoDustThouShaltReturnQuest = null;

            return
                new PrioritySelector(
                    new Decorator(
                        ctx =>
                        (untoDustThouShaltReturnQuest = Me.QuestLog.GetQuestById(UntoDustThouShaltReturnQuestId)) != null && !Me.Combat &&
                        (whiteMane = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == HighInquisitorWhitemaneId && u.IsDead)) != null,
                        new PrioritySelector(
                            new Decorator(ctx => !untoDustThouShaltReturnQuest.IsCompleted && whiteMane.Distance > 5, new Action(ctx => Navigator.MoveTo(whiteMane.Location))),
                            new Decorator(
                                ctx => !untoDustThouShaltReturnQuest.IsCompleted && whiteMane.Distance <= 5,
                                new Action(ctx => Me.BagItems.FirstOrDefault(u => u.Entry == BladesOfTheAnointedId).UseContainerItem())))));
        }

        [EncounterHandler(59706, "Fuel Tank", Mode = CallBehaviorMode.Proximity, BossRange = 10)]
        public Composite FuelTankEncounter()
        {
            return new Decorator<WoWUnit>(
                fuelTank => fuelTank.TransportGuid == 0 && Me.Combat,
                new PrioritySelector(
                    new Decorator<WoWUnit>(fuelTank => fuelTank.WithinInteractRange, new Helpers.Action<WoWUnit>(fuelTank => fuelTank.Interact())),
                    new Decorator<WoWUnit>(fuelTank => !fuelTank.WithinInteractRange, new Helpers.Action<WoWUnit>(fuelTank => Navigator.MoveTo(fuelTank.Location)))));
        }

        [EncounterHandler(64838, "Hooded Crusader", Mode = CallBehaviorMode.Proximity)]
        [EncounterHandler(64827, "Hooded Crusader", Mode = CallBehaviorMode.Proximity)]
        public Composite HoodedCrusaderEncounter()
        {
            WoWUnit unit = null;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                // pickup quests.
                new Decorator(ctx => unit.QuestGiverStatus == QuestGiverStatus.Available, ScriptHelpers.CreatePickupQuest(() => unit)),
                new Decorator(ctx => unit.QuestGiverStatus == QuestGiverStatus.TurnIn, ScriptHelpers.CreateTurninQuest(() => unit)));
        }

        [EncounterHandler(64842, "Lilian Voss", Mode = CallBehaviorMode.Proximity)]
        public Composite LilianVossEncounter()
        {
            return new PrioritySelector(new Decorator<WoWUnit>(unit => unit.QuestGiverStatus == QuestGiverStatus.TurnIn, ScriptHelpers.CreateTurninQuest(64842)));
        }

        [EncounterHandler(64839, "Lilian Voss", Mode = CallBehaviorMode.Proximity)]
        public Composite LilianVossEncounter2()
        {
            return new PrioritySelector(new Decorator<WoWUnit>(unit => unit.QuestGiverStatus == QuestGiverStatus.TurnIn, ScriptHelpers.CreateTurninQuest(64839)));
        }

        [EncounterHandler(59223, "Brother Korloff", Mode = CallBehaviorMode.Proximity, BossRange = 100)]
        public Composite BrotherKorloffEncounter()
        {
            WoWUnit boss = null;
            const int blazingFists = 114807;
            WoWUnit[] trash = null;

            AddAvoidObject(
                ctx => true,
                30,
                o =>
                o.Entry == BrotherKorloffId && !o.ToUnit().Combat && o.ToUnit().IsAlive &&
                ScriptHelpers.GetUnfriendlyNpsAtLocation(() => o.Location, 30, u => u.Entry != BrotherKorloffId).Any());

            AddAvoidObject(ctx => true, 10, u => u.Entry == BrotherKorloffId && u.ToUnit().CastingSpellId == FirestormKickId && u.ToUnit().CurrentTargetGuid != Me.Guid);
            AddAvoidObject(ctx => true, 6, u => u.Entry == BrotherKorloffId && u.ToUnit().CastingSpellId == blazingFists && u.ToUnit().CurrentTargetGuid != Me.Guid, u => u.Location.RayCast(u.Rotation, 3));
            AddAvoidObject(ctx => true, 3, ScorchedEarth);

            return new PrioritySelector(
                ctx =>
                {
                    boss = ctx as WoWUnit;
                    trash = ScriptHelpers.GetUnfriendlyNpsAtLocation(() => boss.Location, 40, u => u.Entry != BrotherKorloffId).ToArray();
                    return boss;
                },
                new Decorator(
                    ctx => !boss.Combat && trash.Any() && Me.IsTank() && !Me.Combat,
                    new PrioritySelector(
                        new Decorator(
                            ctx => trash.Any(t => t.Location.Distance(boss.Location) >= 25),
                            ScriptHelpers.CreateClearArea(() => boss.Location, 40, u => u != boss && u.Location.Distance(boss.Location) >= 25)),
                        new Decorator(ctx => !trash.Any(t => t.Location.Distance(boss.Location) >= 25), new ActionAlwaysSucceed()))),
                new Decorator(ctx => boss.Combat && Me.IsTank() && Targeting.Instance.IsEmpty(), new ActionAlwaysSucceed()));
        }

        [ObjectHandler(214296, "Blade of the Anointed")]
        [ObjectHandler(214284, "Blade of the Anointed")]
        public Composite BladeoftheAnointedHandler()
        {
            WoWGameObject blade = null;

            return new PrioritySelector(
                ctx => blade = ctx as WoWGameObject,
                new Decorator(
                    ctx => blade.CanLoot && !blade.InUse && !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(blade.Location),
                    ScriptHelpers.CreateInteractWithObject(() => blade, 2)));
        }

        /* disabled due to pathising issues. 
        [EncounterHandler(64855, "Blade of the Anointed", Mode = CallBehaviorMode.Proximity, BossRange = 35)]
        [EncounterHandler(64854, "Blade of the Anointed", Mode = CallBehaviorMode.Proximity, BossRange = 35)]
        public Composite BladeoftheAnointedEncounter()
        {
            WoWUnit blade = null;

            return new PrioritySelector(ctx => blade = ctx as WoWUnit,
                new Decorator(ctx => blade.QuestGiverStatus == QuestGiverStatus.TurnIn && Targeting.Instance.IsEmpty() && !ScriptHelpers.WillPullAggroAtLocation(blade.Location),
                    ScriptHelpers.CreateTurninQuest(() => blade)),

                new Decorator(ctx => blade.QuestGiverStatus == QuestGiverStatus.Available && Targeting.Instance.IsEmpty() && !ScriptHelpers.WillPullAggroAtLocation(blade.Location),
                    ScriptHelpers.CreatePickupQuest(() => blade))
                    );
        }

         */

        [LocationHandler(836.4258f, 605.6751f, 13.26288f, 30, "Pull Groups out of chapel")]
        public Composite ClearChapelBehavior()
        {
            var waitLoc = new WoWPoint(836.4258f, 605.6751f, 13.26288f);
            var pullToLoc = new WoWPoint(837.4616, 590.2504, 13.28209);

            WoWUnit pullUnit = null;
            return new PrioritySelector(
                ctx => pullUnit = ScriptHelpers.GetUnfriendlyNpsAtLocation(() => waitLoc, 40).FirstOrDefault(),
                ScriptHelpers.CreatePullNpcToLocation(
                    ctx => !ScriptHelpers.IsBossAlive("Brother Korloff") && pullUnit != null, ctx => true, () => pullUnit, () => pullToLoc, () => waitLoc, 5));
        }

        [EncounterHandler(60040, "Commander Durand", Mode = CallBehaviorMode.Proximity)]
        [EncounterHandler(60106, "Commander Durand", Mode = CallBehaviorMode.Proximity)]
        public Composite CommanderDurandEncounter()
        {
            WoWUnit zealot = null;
            return new PrioritySelector(
                // kill the scarlet zealot that rezes a Judicator before engaging Commander Durand.
                new Decorator(
                    ctx => Me.IsTank() && !Me.Combat && !_checkForZealot,
                    new PrioritySelector(
                        new Decorator(ctx => Me.Location.Distance(_scarletZealotLoc) <= 10, new Action(context => _checkForZealot = true)),
                        new Decorator(ctx => Me.Location.Distance(_scarletZealotLoc) > 10, new Action(context => ScriptHelpers.MoveTankTo(_scarletZealotLoc))))));
        }

        [EncounterHandler(3977, "High Inquisitor Whitemane")]
        public Composite HighInquisitorWhitemaneEncounter()
        {
            WoWUnit boss = null;
            const int massResurrection = 113134;
            const int heal = 12039;
            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateInterruptCast(() => boss, massResurrection, heal),
                ScriptHelpers.CreateDispellParty("Power Word: Shield", ScriptHelpers.PartyDispellType.Magic));
        }

        #region Thalnos the Soulrender

        private const uint ThalnostheSoulrenderId = 59789;

        [EncounterHandler(59789, "Thalnos the Soulrender")]
        public Composite ThalnostheSoulrenderEncounter()
        {
            const int spiritGaleSpellId = 115289;
            const uint spiritGaleObjectId = 2857;
            WoWUnit boss = null;
            AddAvoidObject(ctx => true, 4, spiritGaleObjectId);

            AddAvoidLocation(
                ctx => true,
                2,
                m => ((WoWMissile)m).ImpactPosition,
                () => WoWMissile.InFlightMissiles.Where(m => boss != null && boss.IsValid && m.CasterGuid == boss.Guid && m.SpellId == spiritGaleSpellId));

            return new PrioritySelector(
                ctx => boss = ctx as WoWUnit,
                ScriptHelpers.CreateDispellParty("Evict Soul", ScriptHelpers.PartyDispellType.Magic),
                ScriptHelpers.CreateInterruptCast(() => boss, spiritGaleSpellId));
        }

        #endregion
    }

    #endregion

    #region Heroic Difficulty

    public class ScarletMonasteryHeroic : ScarletMonastery
    {
        #region Overrides of Dungeon

        public override uint DungeonId
        {
            get { return 474; }
        }

        #endregion
    }

    #endregion

}